﻿using Candy.Model;
using GalaSoft.MvvmLight.Command;
using System;
using System.ComponentModel;
using System.ServiceModel.DomainServices.Client;

namespace Candy.ViewModel
{
    public abstract class EntityEditViewModel<T> : EntityViewModelBase<T> where T : Entity
    {
        private bool _isBusy;
        public bool IsBusy
        {
            get
            {
                return _isBusy;
            }
            set
            {
                _isBusy = value;
                RaisePropertyChanged("IsBusy");
                CancelEdit.RaiseCanExecuteChanged();
                Save.RaiseCanExecuteChanged();
            }
        }


        private IEditableObject EditableObject
        {
            get
            {
                return Entity as IEditableObject;
            }
        }

        private bool _isEditing;
        public bool IsEditing
        {
            get
            {
                return _isEditing;
            }
            set
            {
                if (_isEditing == true && value == false)
                    EditableObject.EndEdit();
                if (_isEditing == false && value == true)
                    EditableObject.BeginEdit();

                _isEditing = value;
                RaisePropertyChanged("IsEditing");
                CancelEdit.RaiseCanExecuteChanged();
                Save.RaiseCanExecuteChanged();
                Edit.RaiseCanExecuteChanged();
            }
        }

        public bool IsNewEntitiy
        {
            get
            {
                return Entity.EntityState == EntityState.New;
            }
        }


        //DomainContext _context;
        protected EntitySet<T> Entities;
        protected IDataService _dataService;
        public EntityEditViewModel(IDataService dataService, T entity, EntitySet<T> entities)
        {

            _dataService = dataService;
            Entity = entity;
            //  _context = context;
            Entities = entities;



            //_save = new RelayCommand(ExecSave, CanSave);
        }


        private RelayCommand _edit;
        public RelayCommand Edit
        {
            get
            {
                return _edit ?? (_edit = new RelayCommand(ExecEdit, CanEdit));
            }
        }

        /// <summary>
        /// Checks whether the Edit command is executable
        /// </summary>
        private bool CanEdit()
        {
            return !IsEditing;
        }

        /// <summary>
        /// Executes the Edit command 
        /// </summary>
        protected void ExecEdit()
        {
            IsEditing = true;
        }


        private RelayCommand _cancelEdit;
        public RelayCommand CancelEdit
        {
            get
            {
                return _cancelEdit ?? (_cancelEdit = new RelayCommand(ExecCancelEdit, CanCancelEdit));
            }
        }

        /// <summary>
        /// Checks whether the CancelEdit command is executable
        /// </summary>
        private bool CanCancelEdit()
        {
            return !IsBusy && (IsEditing || IsNewEntitiy);
        }

        /// <summary>
        /// Executes the CancelEdit command 
        /// </summary>
        private void ExecCancelEdit()
        {
            base.ResetError();
            EditableObject.CancelEdit();
            IsEditing = false;
            if (Entity.EntityState == EntityState.New)
                Entities.Remove(Entity);
            if (Canceled != null)
                Canceled(this, EventArgs.Empty);
            //Messenger.Default.Send<EditCompletedMessage>(new EditCompletedMessage(EditResult.Canceled));
        }


        private RelayCommand _save;
        public RelayCommand Save
        {
            get
            {
                return _save ?? (_save = new RelayCommand(ExecSave, CanSave));
            }
        }



        /// <summary>
        /// Checks whether the Save command is executable
        /// </summary>
        private bool CanSave()
        {
            return !IsBusy && (IsEditing || IsNewEntitiy);
        }

        ///<summary>
        ///Executes the Save command 
        ///</summary>
        private void ExecSave()
        {
            base.ResetError();
            IsBusy = true;
            CommitIfValid(iscommited =>
            {
                if (iscommited)
                {
                    //  IsSubmitting = true;
                    SaveEntity(result =>
                    {
                        IsBusy = false;


                        if (result == true)
                        {
                            if (Saved != null)
                                Saved(this, EventArgs.Empty);
                            //   Messenger.Default.Send<EditCompletedMessage>(new EditCompletedMessage(EditResult.Completed));
                        }
                    }
                        );
                }
                else
                {
                    IsBusy = false;
                }

            });
        }


        //protected  virtual void Save()
        //{

        //    CommitIfValid(iscommited =>
        //        {
        //            if (iscommited)
        //            {
        //              //  IsSubmitting = true;
        //                SaveEntity(result =>
        //                    {
        //                     //   IsSubmitting = false;
        //                        if (result == true)
        //                        {
        //                         //   Messenger.Default.Send<EditCompletedMessage>(new EditCompletedMessage(EditResult.Completed));
        //                        }
        //                    }
        //                    );
        //            }

        //        });
        //}





        private void CommitIfValid(Action<bool> callback)
        {
            if (!Entity.HasValidationErrors) //only perfom async vlaidation if clientside is already valid
            {
                OnValidateAsync(isvalid => //call the server side validation implementation from a derived class
                    {
                        if (isvalid)
                        {
                            //commit the changes

                            IsEditing = false;
                            //check again
                            if (Entity.HasValidationErrors)
                            {
                                IsEditing = true;
                                callback(false);
                            }
                            else
                                callback(true);
                        }
                        else
                        {

                            callback(false);
                        }

                    });
            }
            else
            {
                callback(false);
            }
        }

        protected virtual void SaveEntity(Action<bool> result)
        {
            base.ResetError();
            if (Entity.EntityState == EntityState.Modified)
            {
                //update
                _dataService.SubmitAsync(op =>
                {
                    if (op.HasError)
                    {
                        base.HandleDomainError(op, "Saving entitiy failed. Please try again.");
                        result(false);
                    }
                    else
                    {
                        result(true);
                    }
                });

            }
            else if (Entity.EntityState == EntityState.Detached)
            {

                Entities.Add(this.Entity);
                RaisePropertyChanged("IsNewEntitiy");
                _dataService.SubmitAsync(op =>
                {
                    if (op.HasError)
                    {
                        base.HandleDomainError(op, "Saving entitiy failed. Please try again.");
                        result(false);
                    }
                    else
                    {
                        result(true);
                    }
                });
            }
            else if (Entity.EntityState == EntityState.New)
            {
                _dataService.SubmitAsync(op =>
                {
                    if (op.HasError)
                    {
                        base.HandleDomainError(op, "Saving entitiy failed. Please try again.");
                        result(false);
                    }
                    else
                    {
                        result(true);
                    }
                });
            }
            else
            {
                result(true);
            }
        }

        /// <summary>
        /// Implementes the validation routine in a derived class.
        /// </summary>
        protected virtual void OnValidateAsync(Action<bool> callback)
        {
            callback(true);
        }

        public event EventHandler Saved;

        public event EventHandler Canceled;

    }
}

